
#include "cybertron/network/SocketBase.hpp"
#include "cybertron/network/SocketEventHandler.hpp"
#include "cybertron/core/Log.hpp"
#include "AsioSocketAsync.hpp"

CYBERTRON_BEGIN

static std::string sIpEmpty;

CybertronCoreAPI SocketBase::SocketBase()
{
	mId = 0;

	mEventHandlers = std::make_shared<SocketEventHandlerList>();
}

CybertronCoreAPI SocketBase::~SocketBase()
{
	closeThis("SocketBase destructor");
}

CybertronCoreAPI void SocketBase::setImpl(std::shared_ptr<AsioSocketAsync> pImpl)
{
	if (mpImpl != nullptr)
	{
		throw std::runtime_error("Cannot set socket implementation twice!");
	}

	mpImpl = pImpl;
	mpImpl->setEventHandlers(mEventHandlers);
}

CybertronCoreAPI void SocketBase::addHandler(SocketEventHandlerPtr pHandler)
{
	pHandler->setSocket(shared_from_this());

	mEventHandlers->addHandler(pHandler);
}

CybertronCoreAPI void SocketBase::removeHandler(SocketEventHandlerPtr pHandler)
{
	mEventHandlers->removeHandler(pHandler);
}

CybertronCoreAPI void SocketBase::send(const Message& msg)
{
	if (mpImpl != nullptr)
	{
		mpImpl->send(msg);
	}
}

CybertronCoreAPI void SocketBase::send(
	std::uint16_t msgId,
	const google::protobuf::MessageLite& protobufMsg)
{
	Message msg;
	msg.fromProtobuf(msgId, protobufMsg);

	send(msg);
}

CybertronCoreAPI void SocketBase::sendHeartbeat()
{
	if (mpImpl != nullptr)
	{
		mpImpl->sendHeartbeat();
	}
}

CybertronCoreAPI void SocketBase::close(const char* reason)
{
	closeThis(reason);
}

void SocketBase::closeThis(const char* reason)
{
	if (mpImpl != nullptr)
	{
		try
		{
			mpImpl->close(reason);
			mpImpl = nullptr;
		}
		catch (std::exception& e)
		{
			logError("Close Socket Error: %s", e.what());
		}
	}
}

CybertronCoreAPI const std::string& SocketBase::getRemoteIp() const
{
	if (mpImpl != nullptr)
	{
		return mpImpl->getRemoteIp();
	}

	return sIpEmpty;
}

CybertronCoreAPI int SocketBase::getRemotePort() const
{
	if (mpImpl != nullptr)
	{
		return mpImpl->getRemotePort();
	}

	return 0;
}

CybertronCoreAPI unsigned int SocketBase::getLastMessageTime() const
{
	if (mpImpl != nullptr)
	{
		return mpImpl->getLastMessageTime();
	}

	return 0;
}

CybertronCoreAPI ESocketStatus SocketBase::getStatus() const
{
	if (mpImpl != nullptr)
	{
		return mpImpl->getStatus();
	}

	return ESocketStatus_Closed;
}

CybertronCoreAPI void SocketBase::setId(int id)
{
	mId = id;
}

CybertronCoreAPI int SocketBase::getId() const
{
	return mId;
}
void *SocketBase::getSocket() const
{
	// Disable use deprecated warning, since this is a unit test function
#if CYBERTRON_WIN
#pragma warning(disable : 4996)
#endif
#if CYBERTRON_LINUX
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
	return mpImpl->getSocket();
#if CYBERTRON_WIN
#pragma warning(default : 4996)
#endif
#if CYBERTRON_LINUX
#pragma GCC diagnostic pop
#endif
}
CYBERTRON_END
